#pragma once

#include <type_traits>
#include <unordered_map>
#include <ctime>
#include "skip_list.hpp"
#include "noncopyable.hpp"

template <typename KeyType,
        typename ScoreType,
        size_t max_size  = 1000,
        typename Compare = std::greater<ScoreType>>
class rank_map : public noncopyable
{
    static_assert(max_size > 0);

    struct skip_value_type
    {
        KeyType key_id;
        ScoreType score;
        std::time_t last_update_time;
    };

    struct skip_value_greater
    {
        bool operator()(const skip_value_type& x, const skip_value_type& y) const
        {
            if (x.score == y.score)
            {
                return x.last_update_time < y.last_update_time;
            }
            return Compare {}(x.score, y.score);
        }
    };

public:
    using size_type        = size_t;
    using key_type         = KeyType;
    using score_type       = ScoreType;
    using value_type       = skip_value_type;
    using skip_list_type   = skip_list<skip_value_type, skip_value_greater>;
    using iterator         = typename skip_list_type::iterator;
    using hash_map_type    = std::unordered_map<key_type, iterator>;

public:
    explicit rank_map() : list_(), key_hash_() {}

    [[nodiscard]] inline size_type size() const noexcept { return (list_.size()); };
    [[nodiscard]] inline size_type capacity() const noexcept { return max_size; };
    [[nodiscard]] inline bool empty() const noexcept { return (list_.empty()); };
    [[nodiscard]] inline iterator begin() const noexcept { return (list_.begin()); };
    [[nodiscard]] inline iterator end() const noexcept { return (list_.end()); };

public:
    /// @brief 清除排行数据
    void clear()
    {
        list_.clear();
        key_hash_.clear();
    }

    /// @return 第一个值表示修改前的排名,第二个值表示修改后的排名。0:表示没有排名
    std::pair<size_type, size_type> insert_or_assign(const key_type& key, const score_type& score) noexcept
    {
        auto iter_hash = key_hash_.find(key);
        if(iter_hash == key_hash_.end())
        {
            // 排行已满并且评分不大于最后一名的评分，则不上榜
            if(list_.size() >= capacity() && !Compare {}(score, list_.back().score))
            {
                return { 0, 0 };
            }

            auto iter = list_.template emplace(key, score, std::time(nullptr));
            key_hash_[key] = iter;

            // 排行已满，则删除最后一名
            if(list_.size() > capacity())
            {
                auto iter_back = list_.end();
                erase(--iter_back);
            }

            return { 0, rank(iter) };
        }
        else
        {
            auto iter = iter_hash->second;
            auto prev_rank = rank(iter);

            // 评分无变化，不更新
            if(score == iter->score)
            {
                return { prev_rank, prev_rank };
            }

            // 更新跳表
            list_.erase(iter);
            iter = list_.template emplace(key, score, std::time(nullptr));
            key_hash_[key] = iter;

            return { prev_rank, rank(iter) };
        }
    }

    size_type rank(const key_type& key) const noexcept
    {
        auto iter_hash = key_hash_.find(key);
        return iter_hash == key_hash_.end() ? 0 : rank(iter_hash->second);
    }

    size_type rank(iterator iter) const noexcept { return iter == end() ? 0 : list_.index(iter) + 1; }

    iterator find(const key_type& key) const noexcept
    {
        auto iter_hash = key_hash_.find(key);
        return iter_hash == key_hash_.end() ? end() : iter_hash->second;
    }

    iterator find_by_rank(size_type rank) const noexcept { return list_.find_by_index(rank - 1); }

    iterator erase(iterator iter) noexcept
    {
        if(iter != end())
        {
            key_hash_.erase(iter->key_id);
            return list_.erase(iter);
        }
        return end();
    }

    size_type erase(const key_type& key) noexcept
    {
        auto old_size = size();
        erase(find(key));
        return old_size - size();
    }

    template <typename Pred>
    size_type erase_if(Pred pred) noexcept
    {
        for (auto iter = begin(); iter != end();)
        {
            if (pred(std::as_const(*iter)))
            {
                iter = erase(iter);
            }
            else
            {
                --iter;
            }
        }
    }

    const auto& list() const
    {
        return list_;
    }

private:
    skip_list_type list_;
    hash_map_type key_hash_;
};
